﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// The Array class supports methods to work with arrays.  
    /// </summary>
    [Serializable]
    public static class Arrays
    {
        /// <summary>
        /// Adds a prefix to each entry of an array.
        /// </summary>
        /// <param name="array">The array.</param>
        /// <param name="prefix">The prefix.</param>
        /// <returns>The array which includes the new entries.</returns>
        public static String[] AddPrefix(String[] array, String prefix)
        {
            if (array == (string[]) null)
            {
                throw new ArgumentNullException("array");
            }

            for (int i = 0; i < array.Length; i++)
            {
                array[i] = prefix + array[i];
            }

            return array;
        }

        /// <summary>
        /// Converts an array of Integer to an array of type String.
        /// </summary>
        /// <param name="array">The array to convert.</param>
        /// <returns>The converted array.</returns>
        public static String[] ToStringArray(int[] array)
        {
            if (array == (int[]) ((object) null))
            {
                throw new ArgumentNullException("array");
            }

            String[] result = new String[array.Length];

            for (int i = 0; i < array.Length; i++)
            {
                result[i] = array[i].ToString();
            }

            return result;
        }

        /// <summary>
        /// Converts an array of double to an array of type string.
        /// </summary>
        /// <param name="array">The array to convert.</param>
        /// <returns>The converted array.</returns>
        public static String[] ToStringArray(double[] array)
        {
            if (array == (double[]) ((object) null))
            {
                throw new ArgumentNullException("array");
            }

            String[] result = new String[array.Length];

            for (int i = 0; i < array.Length; i++)
            {
                result[i] = array[i].ToString();
            }

            return result;
        }

        /// <summary>
        /// Converts an array of Integer to an array of type Double.
        /// </summary>
        /// <param name="array">The array to convert.</param>
        /// <returns>The converted array.</returns>
        public static double[] ToDoubleArray(int[] array)
        {
            if (array == (int[]) ((object) null))
            {
                throw new ArgumentNullException("array");
            }

            double[] result = new double[array.Length];

            for (int i = 0; i < array.Length; i++)
            {
                result[i] = (double) array[i];
            }

            return result;
        }

        /// <summary>
        /// Converts an array of double to an array of type Integer.
        /// </summary>
        /// <param name="array">The array to convert.</param>
        /// <returns>The converted array.</returns>
        public static int[] ToDoubleArray(double[] array)
        {
            if (array == (double[]) ((object) null))
            {
                throw new ArgumentNullException("array");
            }

            int[] result = new int[array.Length];

            for (int i = 0; i < array.Length; i++)
            {
                result[i] = (int) array[i];
            }

            return result;
        }

        /// <summary>
        /// Calculates the sum of an array.
        /// </summary>
        /// <param name="data">The array.</param>
        /// <returns>The sum of the array.</returns>
        public static int Sum(int[] data)
        {
            if (data == (int[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            int sum = 0;

            for (int i = 0; i < data.Length; i++)
            {
                sum += data[i];
            }

            return sum;
        }

        /// <summary>
        /// Adds a specified value to each value of a numeric array.
        /// </summary>
        /// <param name="data">The numeric array.</param>
        /// <param name="value">The value to add.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Add(double[] data, double value)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            double[] result = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] + value;
            }

            return result;
        }

        /// <summary>
        /// Adds the two specified numeric arrays and returns a new numeric array, 
        /// which includes the results.
        /// </summary>
        /// <param name="data">The first numeric array.</param>
        /// <param name="add">The second numeric array.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Add(double[] data, double[] add)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            if (add == (double[]) ((object) null))
            {
                throw new ArgumentNullException("add");
            }

            double[] result = new double[data.Length];

            if (data.Length != add.Length)
            {
                throw new ArgumentException("The length of both arrays has to be even.");
            }

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] + add[i];
            }

            return result;
        }

        /// <summary>
        /// Subtracts a specified value to each value of a numeric array.
        /// </summary>
        /// <param name="data">The numeric array.</param>
        /// <param name="value">The value to add.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Substract(double[] data, double value)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            double[] result = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] - value;
            }

            return result;
        }

        /// <summary>
        /// Subtracts the two specified numeric arrays and returns a new numeric array, 
        /// which includes the results.
        /// </summary>
        /// <param name="data">The first numeric array.</param>
        /// <param name="sub">The second numeric array.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Substract(double[] data, double[] sub)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            if (sub == (double[]) ((object) null))
            {
                throw new ArgumentNullException("sub");
            }

            double[] result = new double[data.Length];

            if (data.Length != sub.Length)
            {
                throw new ArgumentException("The length of both arrays has to be even.");
            }

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] - sub[i];
            }

            return result;
        }

        /// <summary>
        /// Multiplies a specified value by each value of a numeric array.
        /// </summary>
        /// <param name="data">The numeric array.</param>
        /// <param name="value">The value to add.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Multiply(double[] data, double value)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            double[] result = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] * value;
            }

            return result;
        }

        /// <summary>
        /// Multiplies the two specified numeric arrays and returns a new numeric array, 
        /// which includes the results.
        /// </summary>
        /// <param name="data">The first numeric array.</param>
        /// <param name="multiply">The second numeric array.</param>
        /// <returns>The calculated array with the new numeric values.</returns>
        public static double[] Multiply(double[] data, double[] multiply)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("data");
            }

            if (multiply == (double[]) ((object) null))
            {
                throw new ArgumentNullException("multiply");
            }

            double[] result = new double[data.Length];

            if (data.Length != multiply.Length)
            {
                throw new ArgumentException("The length of both arrays has to be even.");
            }

            for (int i = 0; i < data.Length; i++)
            {
                result[i] = data[i] * multiply[i];
            }

            return result;
        }

        /// <summary>
        /// Searches the minimal value in the array list.
        /// </summary>
        /// <param name="data">The list of numbers in form of an array.</param>
        /// <returns>The minimum value of the list.</returns>
        public static double Min(double[] data)
        {
            if (data == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }
            if (data.Length == 0)
            {
                throw new ArgumentException("list.Length == 0", "list");
            }

            double min = data[0];

            for (int i = 1; i < data.Length; i++)
            {
                if (data[i] < min)
                {
                    min = data[i];
                }
            }

            return min;
        }

        /// <summary>
        /// Searches the maximal value in the array list.
        /// </summary>
        /// <param name="list">The list of numbers in form of an array.</param>
        /// <returns>The maximum value of the list.</returns>
        public static double Max(double[] list)
        {
            if (list == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }
            if (list.Length == 0)
            {
                throw new ArgumentException("list.Length == 0", "list");
            }

            double max = list[0];

            for (int i = 1; i < list.Length; i++)
            {
                if (list[i] > max)
                {
                    max = list[i];
                }
            }

            return max;
        }

        /// <summary>
        /// Sums up all numbers in an array of numbers.
        /// </summary>
        /// <param name="list">The list of numbers.</param>
        /// <returns>The sum of all array elements.</returns>
        public static double Sum(double[] list)
        {
            if (list == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }

            double sum = 0;

            for (int i = 0; i < list.Length; i++)
            {
                sum += list[i];
            }

            return sum;
        }

        /// <summary>
        /// Calculates the product of all values in the array.
        /// </summary>
        /// <param name="list">The list of numbers.</param>
        /// <returns>The product of all values in the array.</returns>
        public static int Product(int[] list)
        {
            if (list == (int[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }

            int product = 1;

            for (int i = 0; i < list.Length; i++)
            {
                product *= list[i];
            }

            return product;
        }

        /// <summary>
        /// Calculates the product of all values in the array.
        /// </summary>
        /// <param name="list">The list of numbers.</param>
        /// <returns>The product of all values in the array.</returns>
        public static double Product(double[] list)
        {
            if (list == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }

            double product = 1;

            for (int i = 0; i < list.Length; i++)
            {
                product *= list[i];
            }

            return product;
        }

        /// <summary>
        /// Calculates the average value of a list of numbers.
        /// </summary>
        /// <param name="list">The list of numbers.</param>
        /// <returns>The average value of the number list.</returns>
        public static double Avg(double[] list)
        {
            if (list == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }

            double avg = 0;

            for (int i = 0; i < list.Length; i++)
            {
                avg += list[i];
            }

            return avg / list.Length;
        }

        /// <summary>
        /// Copies a part of the specified array.
        /// </summary>
        /// <param name="array">The array to copy.</param>
        /// <param name="start">The start index to copy.</param>
        /// <param name="length">The length of copy process.</param>
        /// <returns>A part of the array.</returns>
        public static Object[] Copy(Object[] array, int start, int length)
        {
            if (array.Length < start)
            {
                return null;
            }

            if (array.Length < (start + length))
            {
                length = array.Length - start;
            }

            Object[] result = new Object[length];

            for (int i = start; i < (start + length); i++)
            {
                result[i - start] = array[i];
            }

            return result;
        }

        /// <summary>
        /// Compares two double arrays.
        /// </summary>
        /// <param name="a">The first array.</param>
        /// <param name="b">The second array.</param>
        /// <returns>True, if the vales of the arrays are even otherwise, false.</returns>
        public static bool Compare(double[] a, double[] b)
        {
            // <pex>
            if (b == (double[])null)
                throw new ArgumentNullException("b");
            // </pex>
            if (a.Length != b.Length)
            {
                return false;
            }

            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] != b[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Copies a part of the specified array.
        /// </summary>
        /// <param name="array">The array to copy.</param>
        /// <param name="start">The start index to copy.</param>
        /// <param name="length">The length of copy process.</param>
        /// <returns>A part of the array.</returns>
        public static double[] Copy(double[] array, int start, int length)
        {
            if (array.Length < start)
            {
                return null;
            }

            if (array.Length < (start + length))
            {
                length = array.Length - start;
            }

            double[] result = new double[length];

            for (int i = start; i < (start + length); i++)
            {
                result[i - start] = array[i];
            }

            return result;
        }

        /// <summary>
        /// Rotates the specified array.
        /// </summary>
        /// <param name="array">The array to rotate.</param>
        /// <param name="rotateValue">The number of positions to rotate.</param>
        /// <returns>The rotated array.</returns>
        public static Object[] Rotate(Object[] array, int rotateValue)
        {
            Object[] result = new Object[array.Length];

            for (int i = 0; i < array.Length; i++)
            {
                result[(i + rotateValue) % array.Length] = array[i];
            }

            return result;
        }

        /// <summary>
        /// Checks if the length of the array n is a 2^n integer.
        /// </summary>
        /// <param name="n">The length of the array to check.</param>
        /// <returns>True if the array has a length of 2^n otherwise, false.</returns>
        public static bool Has2PowerNLength(int n)
        {
            return ((n & n - 1) == 0);
        }
    }
}